//Shkolnij Rostislav
package CalculationCore;

import java.util.Vector;

public class CProgramListing {
	private int iListingSizeLimit;
	private int iAddressLenght;
	private boolean isFloatingLimitAllowed;
	private int iPtrOnCurrentRecord;
	private Vector<CProgramRecord> vProgBody;
	private boolean hasEOFhappened;
	private boolean hasNullOperationhappened;
		
	public CProgramListing(int limit, boolean resizelimit){
		setiListingSizeLimit(limit);
		isFloatingLimitAllowed = resizelimit;
		setHasEOFhappened(false);
		setHasNullOperationhappened(false);

		if(getiListingSizeLimit() > 0) {
			vProgBody = new  Vector<CProgramRecord>(getiListingSizeLimit());
			for(int i=0; i<getiListingSizeLimit(); i++)
				vProgBody.add(null);
		} else {
			System.out.println(String.format(" !!! limit = %d must be > 0", limit));
		}
	}
	
	public boolean isHasEOFhappened() {
		return getHasEOFhappened();
	}	
	public int getPointer(int index) {
		int ptr = iPtrOnCurrentRecord + index;
			
			if (ptr >= 0 && ptr < getiListingSizeLimit())
				return ptr;
			else
			{
				System.out.println(String.format(
				" !!! the limitation end (0;%d) of program has been reached: " +
				"ptr = %d", getiListingSizeLimit(), ptr));
				return iPtrOnCurrentRecord;
			}
	}
	public void setPointer(int index, boolean isRelative) {
		int ptr;
			if(isRelative)
				 ptr = iPtrOnCurrentRecord + index;
			else
				ptr = index;
			
			if (ptr >= 0 && ptr < getiListingSizeLimit())
				 iPtrOnCurrentRecord = ptr;
			else
			{
				System.out.println(String.format(
				" !!! the limitation end (0;%d) of program has been reached: " +
				"ptr = %d", getiListingSizeLimit(), ptr));
				setHasEOFhappened(true);
			}
	}
	public int getPointerRelativly(int relindex) {
		return getPointer(relindex);
	}
	public void setPointerRelativly(int relindex) {
		setPointer(relindex, true);
	}
	public int getPointerAbsolutely() {
		return getPointer(0);
	}
	public void setPointerAbsolutely(int absindex) {
		setPointer(absindex, false);
	}
		
	public CProgramRecord getRecordByRelPointer(int relindex) {
		CProgramRecord res = vProgBody.get(getPointer(relindex));
		if(res == null)
			return new CProgramRecord(0x54, false);
		else
			return res;
	}
	public CProgramRecord getRecordByAbsPointer(int absindex) {
		CProgramRecord res = vProgBody.get(absindex);
		if(res == null)
			return new CProgramRecord(0x54, false);
		else
			return res;
	}
	public CProgramRecord getNextRecord() {
		CProgramRecord res = vProgBody.get(getPointer(+1));
		if(res == null)
			return new CProgramRecord(0x54, false);
		else
			return res;
	}
	public CProgramRecord getPrevRecord() {
		CProgramRecord res = vProgBody.get(getPointer(-1));	
		if(res == null)
			return new CProgramRecord(0x54, false);
		else
			return res;
	}
	public void insertProgramRecordIncr(int numb, CButtonEntry info) {
		 vProgBody.set(iPtrOnCurrentRecord, new CProgramRecord(numb, info));
				 
		 if(getPointerRelativly(+1) == -1)
			 if(isFloatingLimitAllowed){
				 setiListingSizeLimit(getiListingSizeLimit() + 1);
				 vProgBody.add(null);
				 setPointerRelativly(+1);
			 } else {
				  System.out.println(String.format(
				  " !!! the last possible command (lim = %d) of program has been set: " +
				  "ptr = %d", getiListingSizeLimit(), iPtrOnCurrentRecord));
				  setHasEOFhappened(true);
			 }
		 else
			 setPointerRelativly(+1);
	}
	public void insertAddressIncr(int numb) {
		 vProgBody.set(iPtrOnCurrentRecord, new CProgramRecord(numb, true));
				 
		 if(getPointerRelativly(+1) == -1)
			 if(isFloatingLimitAllowed){
				 setiListingSizeLimit(getiListingSizeLimit() + 1);
				 vProgBody.add(null);
				 setPointerRelativly(+1);
			 } else {
				  System.out.println(String.format(
				  " !!! the last possible command (lim = %d) of program has been set: " +
				  "ptr = %d", getiListingSizeLimit(), iPtrOnCurrentRecord));
				  setHasEOFhappened(true);
			 }
		 else
			 setPointerRelativly(+1);

	}
	public boolean runProgram(CCPUBlackBox cpuBB) {
		setHasEOFhappened(false);
		setHasNullOperationhappened(false);
		int i;
		for(i = 0;
			cpuBB.isRunning() && 
			!getHasEOFhappened() && 
			getHasNullOperationhappened() && 
			i<20000;
			i++) 
		{ 
			exeStep(cpuBB);
		}

		return getHasEOFhappened() || getHasNullOperationhappened() || (i >= 20000);
	}
	
	public boolean runSingleStep(CCPUBlackBox cpuBB){
		setHasEOFhappened(false);
		setHasNullOperationhappened(false);
		
		exeStep(cpuBB);
		
		return getHasEOFhappened() || getHasNullOperationhappened();
	}
	
	public boolean exeStep(CCPUBlackBox cpuBB){
		int operID;
		CProgramRecord rec = vProgBody.get(iPtrOnCurrentRecord);
		if(rec == null) {
			setHasNullOperationhappened(true);
		} else {
			operID = vProgBody.get(iPtrOnCurrentRecord).getOperationID();
			cpuBB.InternalAutoModeProcessing(operID, false);
		}
		return getHasEOFhappened();
	}
	public String toString() {
		String ret = "Program listing \n";
		int len = getiAddressLenght();
		for(int i=0; i<getiListingSizeLimit(); i++) {
			if(vProgBody.get(i) == null)
				ret += String.format(" (%0"+len+"d) ", i) +
				((i == iPtrOnCurrentRecord)? "*" : " ") +
				" null \n";
			else
				ret += String.format(" (%0"+len+"d) ", i) + 
				((i == iPtrOnCurrentRecord)? "*" : " ") +  
				vProgBody.get(i).toString(len) + "\n";
		}
		return ret;
	}
	public int[] getThreeLastOperations() {
		int[] ret = new int[6];
		CProgramRecord ptr;
		
		if(iPtrOnCurrentRecord>0) {
			ptr = vProgBody.get(iPtrOnCurrentRecord-1);
			if(ptr != null){
				ret[0] = (ptr.isAddressMode())? ptr.getiAddress() : ptr.getOperationID();
				ret[3] = (ptr.isAddressMode())? 1 : 0;
			} else
				ret[0] = -1;
		} else {
			ret[0] = -1;
		}
		    
		if(iPtrOnCurrentRecord>1) {
			ptr = vProgBody.get(iPtrOnCurrentRecord-2);
			if(ptr != null){
				ret[1] = (ptr.isAddressMode())? ptr.getiAddress() : ptr.getOperationID();
				ret[4] = (ptr.isAddressMode())? 1 : 0;
			} else
				ret[1] = -1;
		} else {
			ret[1] = -1;
		}
		
		if(iPtrOnCurrentRecord>2) {
			ptr = vProgBody.get(iPtrOnCurrentRecord-3);
			if(ptr != null){
				ret[2] = (ptr.isAddressMode())? ptr.getiAddress() : ptr.getOperationID();
				ret[5] = (ptr.isAddressMode())? 1 : 0;
			}else
				ret[2] = -1;
		} else {
			ret[2] = -1;
		}
		
		return ret;
	}
	
	public void adjustCapcityForNewOperations(int nNewOperatons) {
		int diff = (iPtrOnCurrentRecord + 1) + nNewOperatons -  getiListingSizeLimit(); 
		if(diff > 0) {
			setiListingSizeLimit(getiListingSizeLimit() + diff);
			for(int i=0; i<diff; i++)
				vProgBody.add(null);
		}
	}
	
	public int getiListingSizeLimit() {
		return iListingSizeLimit;
	}

	private void setiListingSizeLimit(int iListingSizeLimit) {
		this.iListingSizeLimit = iListingSizeLimit;
		setiAddressLenght((int) Math.floor(Math.log10(iListingSizeLimit)) + 1);
	}

	public int getiAddressLenght() {
		return iAddressLenght;
	}

	private void setiAddressLenght(int iAddressLenght) {
		this.iAddressLenght = iAddressLenght;
	}
	
	public Vector<String[]> outputProgramListingToFrame(){
		Vector<String[]> res = new Vector<String[]>(this.iListingSizeLimit);
		String[] str_struct; 
		CProgramRecord src_cmd;
		
		for(int i=0; i<getiListingSizeLimit(); i++) {
			
			src_cmd = vProgBody.get(i);
			str_struct = new String[5];
			
			if(vProgBody.get(i) == null) {
				str_struct[0] = String.format("%0"+iAddressLenght+"d", i);
				str_struct[2] =	"null";
				str_struct[3] =	"";
				str_struct[4] =	"";
			} else {
				if(src_cmd.isAddressMode()){
					str_struct[0] = String.format("%0"+iAddressLenght+"d", i);
					str_struct[2] =	String.format("adr %0"+iAddressLenght+"d",src_cmd.getiAddress());
					str_struct[3] =	"";
					str_struct[4] =	"";
				} else {
					str_struct[0] = String.format("%0"+iAddressLenght+"d", i);
					str_struct[2] =	String.format("0x%02X",src_cmd.getOperationID());
					
					if(src_cmd.getOperationInfo().getsBtnComp() == "")
						str_struct[3] =	"["+src_cmd.getOperationInfo().getsBtnSign()+"]";
					else {
						str_struct[3] =	"["+src_cmd.getOperationInfo().getsBtnComp()+"]" +
										" + " +
										"["+src_cmd.getOperationInfo().getsBtnSign()+"]";
					}
					
					int id = src_cmd.getOperationID(); 
					if((id >= 0x40 && id <= 0x4f) || (id >= 0x60 && id <= 0xef)) {
						str_struct[3] += 
									" + " +
									"["+ String.format("%H", id % 0x10)+"]";
					}
					
					str_struct[4] =	src_cmd.getOperationInfo().getsBtnName();
				}
			}
			str_struct[1] =	((i == iPtrOnCurrentRecord)? "=>" : " ");
			res.add(str_struct);
		}
		return res;
	}
	
	public String outputProgramListingToTXTFile(){
		String res = "";
		 res += String.format("%d,", iListingSizeLimit); 
		 res += String.format("%d,", iPtrOnCurrentRecord); 
		// res += String.format("%d,", iAddressLenght); 
		 res += String.format((isFloatingLimitAllowed? "1" : "0")+","); 

		CProgramRecord src_cmd;
		
		for(int i=0; i<getiListingSizeLimit(); i++) {
			src_cmd = vProgBody.get(i);
		
			if(vProgBody.get(i) == null) {
				res +=  String.format("_;", i);
			} else {
				if(src_cmd.isAddressMode())
					res +=  String.format("a%d;", src_cmd.getiAddress());
				else
					res +=  String.format("x%d;", src_cmd.getOperationID());
			}
		}
		
		//System.out.println("output program listing to TXT file^\n"+res);
		
		return res;
	}
	
	public boolean inputProgramListingFromTXTFile(String src){
		String[] subsrc;
		String temp;
		boolean bWasError = false;
		vProgBody.clear(); 
				
		try {
			subsrc = src.split(",");
			iListingSizeLimit = Integer.parseInt(subsrc[0]);
			setiAddressLenght((int) Math.floor(Math.log10(iListingSizeLimit)) + 1);
			iPtrOnCurrentRecord = Integer.parseInt(subsrc[1]);
			isFloatingLimitAllowed = (subsrc[2] == "1")? true : false;
			
			temp = subsrc[3];
			subsrc = temp.split(";");
			
			CProgramRecord src_cmd;
			for(int i=0; i<getiListingSizeLimit(); i++) {
					
				if(subsrc[i].charAt(0) == '_') {
					src_cmd = null;
				} else {
					temp = subsrc[i].substring(1);
					
					if(subsrc[i].charAt(0) == 'x')
						src_cmd = new CProgramRecord(Integer.parseInt(temp), false);
					else if(subsrc[i].charAt(0) == 'a')
						src_cmd = new CProgramRecord(Integer.parseInt(temp), true);
					else
						throw new Exception("!!! Error occured during reading from TXT file");
				}
				
				vProgBody.add(src_cmd);
			}
		} catch(Exception e) {
			System.out.println("output program listing to TXT file");
		}
		return bWasError ;
	}

	public boolean getHasEOFhappened() {
		return hasEOFhappened;
	}

	public void setHasEOFhappened(boolean hasEOFhappened) {
		this.hasEOFhappened = hasEOFhappened;
	}

	public boolean getHasNullOperationhappened() {
		return hasNullOperationhappened;
	}

	public void setHasNullOperationhappened(boolean hasNullOperationhappened) {
		this.hasNullOperationhappened = hasNullOperationhappened;
	}

}
